﻿using MCUPackage;
using MCUPackage.Utils;
using Newtonsoft.Json;
using SxPackages.STC32G.DesignEditor;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;

namespace SxPackages.STC32G.Models
{
	[Serializable]
	class LCMOptions
	{
		protected STC32GQFP64 mParent = null;

		/// <summary>
		/// 使用的引脚名集合
		/// </summary>
		protected string[] mUseFnPins { get; set; }
		/// <summary>
		/// 使用的数据引脚名集合
		/// </summary>
		protected string[] mUseDataPins { get; set; }
		/// <summary>
		/// 其他控制引脚集合
		/// </summary>
		protected string[] mExtPins { get; set; }

		private bool mEnable = false;
		[Category("TFT彩屏配置"),
		DisplayName("使能"),
		DefaultValue(false),
		JsonProperty(Order = 100),
		Description("是否使能TFT彩屏接口功能")]
		public bool Enable
		{
			get => mEnable;
			set
			{
				if (value != mEnable)
				{
					if (value)
					{
						if (mParent.PinIsUsedTips(mUseFnPins))
						{
							return;
						}
						if (mParent.PinIsUsedTips(mUseDataPins))
						{
							return;
						}
						if (TemplateHelper.PinsIsRepeat(mExtPins))
						{
							return;
						}
						if (mParent.PinIsUsedTips(mExtPins))
						{
							return;
						}
						// 设置引脚用途
						SetFnPins(mUseFnPins);
						SetDataPins(mUseDataPins);
						SetExtPins(mExtPins);
					}
					else
					{
						// 清除占用的功能脚
						mParent.ResetPins(mUseFnPins);
						mParent.ResetPins(mUseDataPins);
						mParent.ResetPins(mExtPins);
					}
					mEnable = value;
				}
			}
		}

		/// <summary>
		/// 切换引脚
		/// </summary>
		/// <param name="aNew"></param>
		/// <param name="aOld"></param>
		/// <param name="aPurpose"></param>
		/// <returns></returns>
		private bool SwitchPin(string aNew, string aOld, PinPurpose aPurpose)
		{
			if (this.Enable)
			{
				if (mParent.PinIsUsedTips(aNew))
				{
					return false;
				}
				mExtPins[aPurpose - PinPurpose.LCM_RST] = aNew;
				SetExtPins(mExtPins);
				// 清除占用的功能脚
				mParent.ResetPins(aOld);
			}
			else
			{
				mExtPins[aPurpose - PinPurpose.LCM_RST] = aNew;
			}
			return true;
		}

		private string mRST = string.Empty;
		[Category("TFT彩屏配置"),
		DisplayName("复位引脚"),
		Description("LCM接口复位引脚"),
		TypeConverter(typeof(PinsConvertor))]
		public string RST
		{
			get => mRST;
			set
			{
				if (value != mRST)
				{
					if (!SwitchPin(value, mRST, PinPurpose.LCM_RST))
					{
						return;
					}
					mRST = value;
				}
			}
		}

		private string mCS = string.Empty;
		[Category("TFT彩屏配置"),
		DisplayName("片选引脚"),
		Description("LCM接口片选引脚"),
		TypeConverter(typeof(PinsConvertor))]
		public string CS
		{
			get => mCS;
			set
			{
				if (value != mCS)
				{
					if (!SwitchPin(value, mCS, PinPurpose.LCM_CS))
					{
						return;
					}
					mCS = value;
				}
			}
		}

		private LCMFnPins mFnPins = LCMFnPins.RS_P45_RD_P44_WR_P42;
		[Category("TFT彩屏配置"),
		DisplayName("控制引脚"),
		DefaultValue(LCMFnPins.RS_P45_RD_P44_WR_P42),
		Description("TFT彩屏接口控制引脚选择")]
		public LCMFnPins FnPins
		{
			get => mFnPins;
			set
			{
				if (value != mFnPins)
				{
					string[] newPins = TemplateHelper.GetPins(value);
					string[] oldPins = TemplateHelper.GetPins(mFnPins);
					if (this.Enable)
					{
						if (mParent.PinIsUsedTips(newPins))
						{
							return;
						}
						SetFnPins(newPins);
						// 清除占用的功能脚
						mParent.ResetPins(oldPins);
					}

					mFnPins = value;
					mUseFnPins = TemplateHelper.GetPins(value);
				}
			}
		}

		private LCMDataPins mDataPins = LCMDataPins.P2;
		[Category("TFT彩屏配置"),
		DisplayName("数据引脚"),
		DefaultValue(LCMDataPins.P2),
		Description("TFT彩屏接口数据引脚选择")]
		public LCMDataPins DataPins
		{
			get => mDataPins;
			set
			{
				if (value != mDataPins)
				{
					if (BitWide && value > LCMDataPins.P6)
					{
						GuiUtils.MsgBox("8位模式下仅能选择P2或P6的8个引脚作为数据口!");
						return;
					}
					string[] newPins = TemplateHelper.GetPins(value);
					string[] oldPins = TemplateHelper.GetPins(mDataPins);
					if (this.Enable)
					{
						// 清除占用的功能脚, 防止与新设置冲突
						mParent.ResetPins(oldPins);
						if (mParent.PinIsUsedTips(newPins))
						{
							SetDataPins(oldPins);
							return;
						}
						SetDataPins(newPins);
					}

					mDataPins = value;
					mUseDataPins = TemplateHelper.GetPins(value);
				}
			}
		}

		[Category("TFT彩屏配置"),
		DisplayName("接口模式"),
		DefaultValue(LCMModes.I8080),
		Description("TFT彩屏接口模式选择")]
		public LCMModes Mode { get; set; } = LCMModes.I8080;

		private bool mBitWide = true;
		[Category("TFT彩屏配置"),
		DisplayName("数据宽度"),
		DefaultValue(true),
		Description("通信接口的数据宽度是否为8位, 否则为16位数据宽度")]
		public bool BitWide
		{
			get => mBitWide;
			set
			{
				if (value != mBitWide)
				{
					if (value)
					{
						DataPins = LCMDataPins.P2;
						if (DataPins != LCMDataPins.P2)
						{
							// 未设置成功
							return;
						}
					}
					else
					{
						DataPins = LCMDataPins.P2_P0;
						if (DataPins != LCMDataPins.P2_P0)
						{
							return;
						}
					}

					// 设置成功
					mBitWide = value;
				}
			}
		}

		private int mSetupTime = 2;
		[Category("TFT彩屏配置"),
		DisplayName("通信建立时间"),
		DefaultValue(2),
		Description("通信接口的通信数据建立时间(时钟数), 取值0~7")]
		public int SetupTime
		{
			get => mSetupTime;
			set
			{
				if (value != mSetupTime)
				{
					if (value < 0 || value > 7)
					{
						return;
					}
					mSetupTime = value;
				}
			}
		}

		private int mHoldTime = 1;
		[Category("TFT彩屏配置"),
		DisplayName("通信保持时间"),
		DefaultValue(1),
		Description("通信接口的通信数据保持时间(时钟数), 取值0~3")]
		public int HoldTime
		{
			get => mHoldTime;
			set
			{
				if (value != mHoldTime)
				{
					if (value < 0 || value > 3)
					{
						return;
					}
					mHoldTime = value;
				}
			}
		}

		[Category("TFT彩屏配置"),
		DisplayName("中断使能"),
		DefaultValue(true),
		Description("是否使能LCM功能中断")]
		public bool EnableTI { get; set; } = true;

		[Category("TFT彩屏配置"),
		DisplayName("中断优先级"),
		Description("中断优先级等级设置"),
		DefaultValue(PriorityTypes.最低级)]
		public PriorityTypes Priority { get; set; } = PriorityTypes.最低级;

		[Category("TFT彩屏配置"),
		DisplayName("DMA使能"),
		DefaultValue(true),
		Description("是否开启DMA发送数据到彩屏功能, 建议开启")]
		public bool EnableDMA { get; set; } = true;

		[Category("TFT彩屏配置"),
		DisplayName("DMA中断优先级"),
		Description("DMA中断优先级等级设置"),
		DefaultValue(PriorityTypes.最低级)]
		public PriorityTypes DMAPriority { get; set; } = PriorityTypes.最低级;

		[Category("TFT彩屏配置"),
		DisplayName("总线优先级"),
		Description("DMA数据总线访问优先级等级设置"),
		DefaultValue(PriorityTypes.最低级)]
		public PriorityTypes BusPriority { get; set; } = PriorityTypes.最低级;

		private int mVRamSize = 4096;
		[Category("TFT彩屏配置"),
		DisplayName("显存大小"),
		DefaultValue(4096),
		Description("DMA自动发送数据到显示的内存区域大小(字节数), 由于每个点需要两个字节表示颜色, 所以必须为偶数大小")]
		public int VRamSize
		{
			get => mVRamSize;
			set
			{
				if (value != mVRamSize)
				{
					if (value < 10 || value > (32 * 1024) || (value % 2) != 0)
					{
						return;
					}
					mVRamSize = value;
				}
			}
		}

		protected void SetFnPins(string[] aPins)
		{
			PinPurpose purpose = PinPurpose.LCM_RS;
			foreach (var pin in aPins)
			{
				var opt = mParent.SetPinPurpose(pin, purpose);
				opt.Mode = GPIOMode.Push;
				opt.Level = GPIOLevel.High;
				purpose++;
			}
		}

		protected void SetDataPins(string[] aPins)
		{
			PinPurpose purpose = aPins.Length < 9 ? PinPurpose.LCM_DATA7 : PinPurpose.LCM_DATA15;
			foreach (var pin in aPins)
			{
				var opt = mParent.SetPinPurpose(pin, purpose);
				opt.Mode = GPIOMode.Standard;
				purpose++;
			}
		}

		protected void SetExtPins(string[] aPins)
		{
			if (aPins == null || aPins.Length < 1)
			{
				return;
			}

			PinPurpose purpose = PinPurpose.LCM_RST;
			foreach (var pin in aPins)
			{
				var opt = mParent.SetPinPurpose(pin, purpose);
				if (opt != null)
				{
					opt.Mode = GPIOMode.Push;
					opt.Level = GPIOLevel.High;
					purpose++;
				}
			}
		}

		/// <summary>
		/// 设置的引脚是否有效
		/// </summary>
		/// <returns></returns>
		public bool PinsValid()
		{
			foreach (var pin in mExtPins)
			{
				if (mParent.GetPin(pin) == null)
				{
					return false;
				}
			}

			return true;
		}

		public LCMOptions(STC32GQFP64 aParent)
		{
			this.mParent = aParent;
			this.mUseFnPins = TemplateHelper.GetPins(mFnPins);
			this.mUseDataPins = TemplateHelper.GetPins(mDataPins);
			this.mExtPins = new string[] { this.RST, this.CS };
		}

		/// <summary>
		/// 初始化模板
		/// </summary>
		private const string TEMPLATE =
@"	attr.LCM_Enable = ENABLE;				//LCM接口使能  	ENABLE,DISABLE
	attr.LCM_Mode = {0};			//LCM接口模式  	MODE_I8080,MODE_M6800
	attr.LCM_Bit_Wide = BIT_WIDE_{1};	//LCM数据宽度  	BIT_WIDE_8,BIT_WIDE_16
	attr.LCM_Setup_Time = {2};					//LCM通信数据建立时间  	0~7
	attr.LCM_Hold_Time = {3};					//LCM通信数据保持时间  	0~3

	LCM_CTRL_SW(LCM_CTRL{4});	//LCM_CTRL_P45_P44_P42,LCM_CTRL_P45_P37_P36,LCM_CTRL_P40_P44_P42,LCM_CTRL_P40_P37_P36
	LCM_DATA_SW({5});		//LCM_D8_NA_P2,LCM_D8_NA_P6 / LCM_D16_P2_P0,LCM_D16_P6_P2,LCM_D16_P2_P0P4,LCM_D16_P6_P7

	LCM_Inilize(&attr);		//初始化
	NVIC_LCM_Init({6}, {7});		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3";

		private const string DMA_TEMPLATE =
@"	dma.DMA_Enable = ENABLE;			//DMA使能  	ENABLE,DISABLE
	dma.DMA_Length = (uint16_t)(LCM_DMA_MAX * 2 - 1);	//DMA传输总字节数  	(0~65535) + 1, 不要超过芯片 xdata 空间上限
	dma.DMA_Tx_Buffer = (uint16_t)LCMRam;		//发送数据存储地址
	dma.DMA_Rx_Buffer = (uint16_t)LCMRcvBuffer;		//接收数据存储地址
	DMA_LCM_Inilize(&dma);		//初始化
	NVIC_DMA_LCM_Init(ENABLE, {0}, {1});		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0~Priority_3; 总线优先级(低到高) Priority_0~Priority_3";

		private const string VAR_TEMPLATE =
@"#define LCM_DMA_MAX		{0}
uint16_t xdata LCMRcvBuffer[8] = {{0x00}};
uint16_t xdata LCMRam[LCM_DMA_MAX];";

		/// <summary>
		/// 创建无效的头文件
		/// </summary>
		/// <param name="aPath"></param>
		public bool CreateLibFiles(string aPath)
		{
			string file = "STC32G_LCM.h";
			var pairs = TemplateHelper.CreatePairs();

			pairs["LCM_RS"] = mUseFnPins[0].Replace('.', '^');
			pairs["LCM_RD"] = mUseFnPins[1].Replace('.', '^');
			pairs["LCM_WR"] = mUseFnPins[2].Replace('.', '^');

			pairs["LCM_RST"] = mParent.GetPin(mExtPins[0]) == null ? "P6^6" : mExtPins[0].Replace('.', '^');
			pairs["LCM_CS"] = mParent.GetPin(mExtPins[1]) == null ? "P6^6" : mExtPins[1].Replace('.', '^');

			return mParent.CreateByTemplate(PrjPathTypes.Drivers_STC32_Inc, aPath, file, false, pairs);
		}

		public Boolean AppendConfig(string aPath, List<String[]> aLst)
		{
			if (!this.Enable)
			{
				return false;
			}

			Dictionary<string, List<string>> lst = new Dictionary<string, List<string>>();
			foreach (var pin in mExtPins)
			{
				var opt = mParent.GetPinOption(pin);
				if (opt != null)
				{
					opt.ToDictionary(lst);
				}
			}
			string pins = string.Empty;
			foreach (var pin in mUseFnPins)
			{
				var opt = mParent.GetPinOption(pin);
				if (opt != null)
				{
					opt.ToDictionary(lst);
					pins += "_";
					pins += pin.Replace(".", "");
				}
			}
			foreach (var pin in mUseDataPins)
			{
				var opt = mParent.GetPinOption(pin);
				if (opt != null)
				{
					opt.ToDictionary(lst);
				}
			}
			if (!CreateLibFiles(aPath))
			{
				return false;
			}

			string name = "LCM_Config";
			StringBuilder funcSb = new StringBuilder();
			StringBuilder varSb = new StringBuilder();
			funcSb.Append(TemplateHelper.GetFunctionCommit("LCM模块初始化"));
			funcSb.AppendLine($"PRIVATE void {name}(void)").AppendLine("{");
			funcSb.AppendLine("\tLCM_InitTypeDef attr;");
			if (this.EnableDMA)
			{
				funcSb.AppendLine("\tDMA_LCM_InitTypeDef dma;");
			}

			funcSb.AppendLine();
			funcSb.Append(GPIOOptions.GetGPIOConfigs(lst));

			funcSb.AppendLine();
			string str = "LCM_D8_NA_P2";
			if (this.DataPins <= LCMDataPins.P6)
			{
				str = $"LCM_D8_NA_{this.DataPins}";
			}
			else if (this.DataPins == LCMDataPins.P2_P0_P47_P46_P43_P41)
			{
				str = "LCM_D16_P2_P0P4";
			}
			else
			{
				str = $"LCM_D16_{this.DataPins}";
			}
			funcSb.AppendFormat(TEMPLATE, this.Mode.ToDescription(), this.BitWide ? 8 : 16, this.SetupTime, this.HoldTime,
								pins, str, this.EnableTI.ToEnable(), this.Priority.ToDescription());
			funcSb.AppendLine();

			if (this.EnableDMA)
			{
				funcSb.AppendLine();
				funcSb.AppendFormat(DMA_TEMPLATE, this.DMAPriority.ToDescription(), this.BusPriority.ToDescription()).AppendLine();
				varSb.AppendFormat(VAR_TEMPLATE, this.VRamSize / 2).AppendLine();
			}

			funcSb.AppendLine("}");
			aLst.Add(new string[] { funcSb.ToString(), $"{name}();", varSb.ToString() });
			return true;
		}
	}
}
